// FILE: Chip8Screen.h by Richard Hughes 29/40/2012
//
// DESC: Defines the Chip8 Screen

#ifndef __chip8screen_h__
#define __chip8screen_h__

// INCLUDES
#include "IInteruptableDevice.h"
#include <assert.h>
#include <memory>

// NAMESPACES
namespace Emulator
{
namespace Chip8
{

enum EmulatorRunningMode;

/*************************************************************/
//
// CLASS: Chip8Screen
//
// DESC: Defines the chip 8 screen
//
/*************************************************************/

class Chip8Screen : public IInteruptableDevice
{
protected:

	int _width; // pixels
	int _height; // pixels

	bool * _screenData;

	// used with SCR, SCL and SCDn instructions
	int _xOffset;
	int _yOffset;
public:

	Chip8Screen()
		: _width(64),
		  _height(32),
		  _xOffset(0),
		  _yOffset(0)
	{
		this->_screenData = new bool[this->_width * this->_height];
		memset(this->_screenData, false, sizeof(bool) * this->_width * this->_height);
	}
	~Chip8Screen()
	{
		if(this->_screenData != NULL)
		{
			delete [] this->_screenData;
			this->_screenData = NULL;
		}
	}

	inline const int GetWidth() const { return this->_width; }
	inline const int GetHeight() const { return this->_height; }

	void SetWidth(int width) { this->_width = width; }
	void SetHeight(int height) { this->_height = height; }

	inline virtual void ClearScreen()
	{
		memset(this->_screenData, false, sizeof(bool) * this->_width * this->_height);
	} // ClearScreen

	inline virtual void UpdateScreen() = 0;

	inline const bool GetPixelValue(int x, int y) const
	{
		// allow for wrapping
		if(x > this->_width)
			x -= this->_width;
		if(x < 0)
			x += this->_width;

		if(y > this->_height)
			y -= this->_height;
		if(y < 0)
			y += this->_height;

		assert(x >= 0 && x < this->_width &&
			   y >= 0 && y < this->_height);

		return this->_screenData[x + (y * this->_width)];
	} // GetPixelValue

	inline const bool SetPixel(int x, int y)
	{
		return this->SetPixel(x, y, true);
	} // SetPixel

	inline const bool UnSetPixel(int x, int y)
	{
		return this->SetPixel(x, y, false);
	} // UnSetPixel

	inline const bool SetPixel(int x, int y, bool value)
	{
		// allow for wrapping
		while(x >= this->_width)
			x -= this->_width;
		while(x < 0)
			x += this->_width;

		while(y >= this->_height)
			y -= this->_height;
		while(y < 0)
			y += this->_height;

		assert(x >= 0 && x < this->_width &&
			   y >= 0 && y < this->_height);

		bool hasPixel = this->_screenData[x + (y * this->_width)];

		this->_screenData[x + (y * this->_width)] ^= value;

		// return true if a pixel was erased (now false)
		return hasPixel && this->_screenData[x + (y * this->_width)] == false;
	} // SetPixel

	void ScrollRight(EmulatorRunningMode mode);
	void ScrollLeft(EmulatorRunningMode mode);
	void ScrollDown(int lines);

}; // Chip8Screen

} // Chip8
} // Emulator

#endif // __chip8screen_h__

// EOF: Interupt_ClearScreen.h